/**
 * Copyright(c) 2014-2016 Basung Information Technology (Shanghai) Co., Ltd.
 * <p>
 * 　　　　　　　　┏┓　　　┏┓+ +
 * 　　　　　　　┏┛┻━━━┛┻┓ + +
 * 　　　　　　　┃　　　　　　　┃
 * 　　　　　　　┃　　　━　　　┃ ++ + + +
 * 　　　　　　 ████━████ ┃+
 * 　　　　　　　┃　　　　　　　┃ +
 * 　　　　　　　┃　　　┻　　　┃
 * 　　　　　　　┃　　　　　　　┃ + +
 * 　　　　　　　┗━┓　　　┏━┛
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃ + + + +
 * 　　　　　　　　　┃　　　┃　　　　Code is far away from bug with the animal protecting
 * 　　　　　　　　　┃　　　┃ + 　　　　神兽保佑,代码无bug
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃　　+
 * 　　　　　　　　　┃　 　　┗━━━┓ + +
 * 　　　　　　　　　┃ 　　　　　　　┣┓
 * 　　　　　　　　　┃ 　　　　　　　┏┛
 * 　　　　　　　　　┗┓┓┏━┳┓┏┛ + + + +
 * 　　　　　　　　　　┃┫┫　┃┫┫
 * 　　　　　　　　　　┗┻┛　┗┻┛+ + + +
 * <p>
 * History:
 * 2016/10/21 下午2:08 Created by leijinghan
 */
package com.jade.bss.service.sms.impl;

import com.jade.bss.base.BssConstants;
import com.jade.bss.base.BssException;
import com.jade.bss.service.sms.VerifyCodeManager;
import com.jade.framework.base.context.ApplicationContextUtils;
import com.jade.framework.base.text.VelocityUtils;
import com.jade.framework.base.util.DataUtils;
import com.jade.framework.base.util.Parameters;
import com.jade.framework.base.util.SimpleParameters;
import com.jade.framework.base.util.UniqueID;
import com.jade.framework.cache.CacheService;
import com.jade.journal.Journal;
import com.jade.journal.JournalUtils;
import com.jade.modules.message.sms.*;
import com.jade.modules.number.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.inject.Named;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * Explain: 天下畅通 验证码调用服务
 * Description:
 *
 * @Author : <a href="mailto:jinghan.lei@basung.com">leijinghan</a>.
 * @Version: 1.0
 * @Date : 2016/10/21.
 */
@Named("bss_telhkVerifyCodeManager")
public class TelhkVerifyCodeManagerImpl implements VerifyCodeManager {
    protected static final String CACHE_PREFIX = "bss.sms.verifycode.";
    protected SmsClient smsClient;
    protected CacheService cacheService;
    //默认10分钟
    protected int timeout = 600;
    //一分钟之内不能发送多条
    protected int interval = 60;
    //一小时内不能发送超过的条数
    protected int maxCountInHour = 5;
    //journal sms
    protected boolean journalSms = false;
    //Journal
    protected Journal journal = JournalUtils.getJournal(SmsJournal.NAME);
    protected Log log = LogFactory.getLog(VerifyCodeManagerImpl.class);
    //模板配置，因为有些通道需要使用模板方式发送短信
    protected Map<String, String> templateMap;

    public void setSmsClient(SmsClient smsClient)
    {
        this.smsClient = smsClient;
    }

    public CacheService getCacheService()
    {
        if (cacheService == null) {
            cacheService = ApplicationContextUtils.getBean("system_cacheService");
        }
        return cacheService;
    }

    public void setCacheService(CacheService cacheService)
    {
        this.cacheService = cacheService;
    }

    public void setTimeout(int timeout)
    {
        this.timeout = timeout;
    }

    public void setInterval(int interval)
    {
        this.interval = interval;
    }

    public void setMaxCountInHour(int maxCountInHour)
    {
        this.maxCountInHour = maxCountInHour;
    }

    public boolean isJournalSms()
    {
        return journalSms;
    }

    public void setJournalSms(boolean journalSms)
    {
        this.journalSms = journalSms;
    }

    public void setTemplateMap(Map<String, String> templateMap)
    {
        this.templateMap = templateMap;
    }

    public String sendVerifyCode(String type, String mobile)
            throws BssException
    {
        if (!NumberUtils.isValidMobile(mobile)) {
            throw new BssException("err.bss.sms.verifycode.invalid_mobile", "invalid mobile number");
        }

        Object[] array = (Object[]) getCacheService().get(getCodeCacheKey(type, mobile));
        String code = UniqueID.nextCode(6);
        Date now = new Date();
        long hour = DateUtils.getFragmentInHours(now, Calendar.DAY_OF_YEAR);
        if (array != null) {
            Date lastSentTime = (Date) array[1];
            if (now.getTime() - lastSentTime.getTime() < interval * 1000) {
                throw new BssException("err.bss.sms.verifycode.frequently", "You just sent verify code!");
            }
            code = (String) array[0];
        }
        long times = DataUtils.getLong(getCacheService().get(getCountCacheKey(type, mobile, hour)), 0);
        if (times >= maxCountInHour) {
            throw new BssException("err.bss.sms.verifycode.toomany", "You have sent verify code too much!");
        }
        String content;
        try {
            content = getSmsContent(type, mobile, code);
        }
        catch (Exception e) {
            throw new BssException("err.bss.sms.verifycode.content", "Can't get sms content", e);
        }

        SmsMessage sms = new SmsMessage();
        sms.setFormat(SmsConstants.FORMAT_UCS2);
        sms.setContent(content);
        sms.setTo(mobile);
        SmsServiceParams serviceParams = new SmsServiceParams();
        if (templateMap != null) {
            serviceParams.setString("template_id", templateMap.get(type));
        }
        serviceParams.setString("param", code);
        SmsResult result = smsClient.send(sms, serviceParams);
        log.info("Send verify code " + code + " to " + mobile + ":" + result);
        if (journalSms) {
            SmsJournal record = new SmsJournal();
            record.setDirection(SmsJournal.MT);
            record.setType("verify_code");
            record.setSubType(type);
            record.setSender(BssConstants.OPERATOR_TYPE_SYSTEM);
            record.setOperator(NumberUtils.getOperator(mobile));
            record.setReceiver(mobile);
            record.setContent(content);
            record.setStatus(result.getCode(0));
            record.setStatusMessage(result.getMessage(0));
            record.setJournalDate(new Date());
            journal.infoEntity(record);
        }

        if (result.isOK(0)) {
            getCacheService().set(getCodeCacheKey(type, mobile), new Object[]{code, now}, timeout);
            getCacheService().incr(getCountCacheKey(type, mobile, hour), 1L, 1L, 3600);
        }
        else {
            throw new BssException("err.bss.sms.verifycode.send", "Can't send sms:" + result.getCode(0));
        }
        return code;
    }

    public String sendVerifyCode(String mobile)
            throws BssException
    {
        return sendVerifyCode("def", mobile);
    }

    public boolean checkVerifyCode(String mobile, String verifyCode)
            throws BssException
    {
        return checkVerifyCode("def", mobile, verifyCode);
    }

    public boolean checkVerifyCode(String type, String mobile, String verifyCode)
            throws BssException
    {
        Object[] array = (Object[]) getCacheService().get(getCodeCacheKey(type, mobile));
        return array != null && StringUtils.equalsIgnoreCase(verifyCode, (String) array[0]);
    }

    protected String getCodeCacheKey(String type, String mobile)
    {
        return CACHE_PREFIX + type + "." + mobile;
    }

    protected String getCountCacheKey(String type, String mobile, long hour)
    {
        return CACHE_PREFIX + type + "." + mobile + "." + hour;

    }

    protected String getSmsContent(String type, String mobile, String code)
            throws Exception
    {
        Parameters params = new SimpleParameters();
        params.setString("mobile", mobile);
        params.setString("type", type);
        params.setString("verify_code", code);
        String operator = NumberUtils.getOperator(mobile);
        params.setString("operator", operator);
        return VelocityUtils.mergeTemplate("sms_verify_code_" + type, params);
    }
}
